<template>
    <teleport to="body" :disabled="!teleported">
        <transition name="context-menu-fade">
            <ul ref="contextMenuRef" class="context-menu" :style="{...style, ...display}" @contextmenu.prevent @mousewheel.stop>
                <template v-for="(item, index) of menus" :key="index">
                    <menu-item v-if="!item.hidden" v-model:active="activeIndex" :index="index" :menu="item" :direction="dynamicDirection" :parent="menus"/>
                </template>
            </ul>
        </transition>
    </teleport>
</template>

<script>
import {computed, defineComponent, nextTick, ref, provide, useAttrs} from "vue";
import {onClickOutside} from '@vueuse/core';
import MenuItem from './menu-item.vue'

export default defineComponent({
    name: "ContextMenu",
    components: {
        MenuItem
    },
    props: {
        data: {
            type: Object,
            default: null
        },
        menus: {
            type: Array,
            default: []
        },
        event: {
            type: Object,
            default: undefined
        },
        position: {
            type: Object,
            default: {}
        },
        minWidth: {
            type: [Number, String],
            default: 220
        },
        maxWidth: {
            type: [Number, String],
            default: 'none'
        },
        zIndex: {
            type: Number,
            default: 3
        },
        direction: {
            type: String,
            default: 'right'
        },
        modelValue: {
            type: Boolean,
            default: false
        },
        teleported: {
            type: Boolean,
            default: false
        },
        menuStyle: {
            type: Object,
            default: undefined
        },
    },
    setup(props, {emit}) {
        const data = computed(() => props.data ?? props.event?.data);
        provide('SelectedData', data);
        const windowWidth = document.documentElement.clientWidth;
        const windowHeight = document.documentElement.clientHeight;
        const contextMenuRef = ref();
        const activeIndex = ref(-1);
        const dynamicDirection = ref(props.direction);
        const _position = computed(() => props.event? {x: props.event.clientX, y: props.event.clientY, width: 0, height: 0}: props.position);
        const style = ref({
            '--level-number': 1,
            left: '0px',
            top: '0px',
            minWidth: `${props.minWidth}${Number(props.minWidth)?'px':''}`,
            maxWidth: props.maxWidth === 'none' ? props.maxWidth : `${props.maxWidth}${Number(props.maxWidth)?'px':''}`,
            zIndex: props.zIndex
        })
        if (props.menuStyle) {
            Object.assign(style.value, props.menuStyle)
        }
        const display = computed(() => {
            let status = 'unset';
            if (props.modelValue) {
                init();
            } else {
                status = 'none';
            }
            return {display: status}
        })

        function leftOpen(menusWidth) {
            let left = _position.value.x - menusWidth;
            dynamicDirection.value = props.direction;
            if (left < 0) {
                left = !_position.value.width? 0 : _position.value.x + _position.value.width
                dynamicDirection.value = 'right';
            }
            return left;
        }

        function rightOpen(windowWidth, menusWidth) {
            let left = _position.value.x + _position.value.width;
            dynamicDirection.value = props.direction;
            if (left + menusWidth > windowWidth) {
                left = !_position.value.width? windowWidth - menusWidth:_position.value.x - menusWidth
                dynamicDirection.value = 'left';
            }
            return left;
        }

        function init() {
            nextTick(() => {
                const menusWidth = contextMenuRef.value.offsetWidth;
                const menusHeight = contextMenuRef.value.offsetHeight;
                if (dynamicDirection.value === 'left') {
                    style.value.left = leftOpen(menusWidth) + 'px';
                } else {
                    style.value.left = rightOpen(windowWidth, menusWidth) + 'px';
                }

                if (_position.value.y + menusHeight <= windowHeight) {
                    style.value.top = _position.value.y + 'px';
                } else {
                    style.value.top = (!_position.value.height? (_position.value.y < menusHeight? 0:_position.value.y - menusHeight):windowHeight - menusHeight) + 'px'
                }
            })
        }


        const onClose = () => {
            emit("update:modelValue", false)
        }
        if (style.value['--level-number'] === 1) {
            provide('onClose', onClose);
        }
        // 点击菜单之外的任一区域都关闭菜单
        onClickOutside(contextMenuRef, () => onClose(), {ignore: []})

        return {
            contextMenuRef,
            style,
            display,
            activeIndex,
            dynamicDirection
        };
    },
});
</script>

<style scoped>
.context-menu {
    position: fixed;
    margin: 0;
    border-radius: 4px;
    background: var(--el-bg-color-overlay);
    border: 1px solid var(--el-border-color-light);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
    z-index: 3000;
    padding: 8px 0;
    user-select: none;
    box-sizing: border-box;
}

.context-menu-fade-enter-active,
.context-menu-fade-leave-active {
    transition: opacity 0.1s ease-in-out;
}

.context-menu-fade-enter-from,
.context-menu-fade-leave-to {
    opacity: 0;
}
</style>
